﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using API;
using AVFoundation;
using CoreFoundation;
using Foundation;
using MobileCoreServices;
using MyExtensions;
using ObjCRuntime;

namespace mb.iOS
{

    public class MyResourceLoaderDS : MyResourceLoader
    {
        public static string SchemaHolder = "jjxx";
        static ILog logger = mb.ServiceLocator.Instance.Get<ILog>();
        AVUrlAsset loadingAsset;
        AVAssetResourceLoadingRequest loadingRequest;
        string realUrl;
        IDataStream stream;
        byte[] keyBuffer;
        bool closed;

        public MyResourceLoaderDS(AVUrlAsset asset, string key)
        {
            closed = false;
            loadingAsset = asset;

            var url = loadingAsset.Url.ToString();
            realUrl = url.Replace(SchemaHolder, "http");

            var cdsMgr = ServiceLocator.Instance.Get<ICacheDataStreamManager>();
            stream = cdsMgr.CreateDataStream(realUrl);

            IStartOption startOption = mb.ServiceLocator.Instance.Get<IStartOption>();
            var raw = startOption.GetBoolValue("movie-raw");
            if (!raw)
                keyBuffer = System.Text.Encoding.ASCII.GetBytes(key);

            asset.ResourceLoader.SetDelegate(this, DispatchQueue.GetGlobalQueue((DispatchQueuePriority) 0x21));
            // logger.Log("media create thread", System.Threading.Thread.CurrentThread.ManagedThreadId, raw);
        }

        public override void Close()
        {
            stream.Close();
            loadingAsset.CancelLoading();
            closed = true;
        }

        #region AV
        public override bool ShouldWaitForLoadingOfRequestedResource(AVAssetResourceLoader resourceLoader, AVAssetResourceLoadingRequest loadingRequest)
        {
            var tid = System.Threading.Thread.CurrentThread.ManagedThreadId;
            // logger.Log($"{tid}/{loadingRequest.GetHashCode()}: AskForLoading");
            Task.Run(() =>
            {
                LoadRequest(loadingRequest);
            });
            return true;
        }

        void LoadRequest(AVAssetResourceLoadingRequest loadingRequest)
        {
            this.loadingRequest = loadingRequest;

            var dreq = loadingRequest.DataRequest;
            var ireq = loadingRequest.ContentInformationRequest;

            var tid = System.Threading.Thread.CurrentThread.ManagedThreadId;
            var logHead = $"{tid}/{loadingRequest.GetHashCode()}:";
            if (dreq != null)
            {
                long offset = dreq.CurrentOffset;
                long length = dreq.RequestedLength - (offset - dreq.RequestedOffset);

                //logger.Log($"{logHead}: load request", dreq.RequestedOffset, dreq.CurrentOffset, dreq.RequestedLength);

                try
                {
                    var buffer = new byte[8 * 1024];
                    // var buffer = new byte[length];
                    long from = dreq.CurrentOffset;
                    long remain = length;
                    while (remain > 0)
                    {
                        //任务被取消，一般是跳进度
                        if (loadingRequest.IsCancelled)
                        {
                            //logger.Log($"{logHead} check canceled");
                            return;
                        }
                        //流已经关闭了，一般是界面被关掉
                        if (closed)
                        {
                            //logger.Log($"{logHead} check closed");
                            return;
                        }

                        int read = stream.Read(from, buffer, 0, buffer.Length);

                        if (ireq != null)
                        {
                            ireq.ContentType = "video/mp4";
                            ireq.ContentLength = stream.GetLength();
                            ireq.ByteRangeAccessSupported = true;
                            //logger.Log($"{logHead}: finish info request", ireq.ContentLength);
                            loadingRequest.FinishLoading();
                            return;
                        }

                        if (keyBuffer != null)
                        {
                            long koff = offset;
                            for (int i = 0; i < read; i++, koff++)
                            {
                                buffer[i] = (byte) (buffer[i] ^ keyBuffer[koff % keyBuffer.Length]);
                            }
                        }

                        var crc32 = new Crc32();
                        var hash = "";
                        // foreach (var b in crc32.ComputeHash(buffer)) hash += b.ToString("x");
                        // logger.Log($"{logHead}: data[{from}-{from+read-1}] fetched, hash[{hash}]");

                        var data = NSData.FromArray(buffer);
                        dreq.Respond(data);

                        remain -= read;
                        from += read;
                    }

                    //logger.Log($"{tid}/{loadingRequest.GetHashCode()}: finish data request");
                    loadingRequest.FinishLoading();
                }
                catch (Exception e)
                {
                    // return false;
                }
            }

        }

        public override void DidCancelLoadingRequest(AVAssetResourceLoader resourceLoader, AVAssetResourceLoadingRequest loadingRequest)
        {
            var tid = System.Threading.Thread.CurrentThread.ManagedThreadId;
            logger.Log($"{tid}/{loadingRequest.GetHashCode()}: cancel request", loadingRequest.DataRequest.CurrentOffset);

        }
        #endregion

    }
}